package com.stars.easyms.monitor.git;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>className: GitPropertiesLoader</p>
 * <p>description: git.properties属性文件加载器</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019-06-05 16:05
 */
@Slf4j
final class GitPropertiesLoader {

    private static final String GIT_RESOURCE_LOCATION = "git.properties";

    private static final String SEPARATOR = "@#%!&";

    private static final Map<ClassLoader, Map<String, Object>> CACHE = new ConcurrentReferenceHashMap<>();

    private GitPropertiesLoader() {
    }

    /**
     * 加载所有的git的properties文件
     *
     * @param classLoader 类加载器
     * @return Map<String, Map < String, Object>> key:url value:属性map
     */
    static Map<String, Object> loadAllGitProperties(@Nullable ClassLoader classLoader) {
        Map<String, Object> result = CACHE.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(GIT_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(GIT_RESOURCE_LOCATION));
            result = new LinkedHashMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Map<String, Object> value = loadGitProperties(resource);
                result.put(url.toString(), value);
            }
            CACHE.put(classLoader, result);
            return result;
        } catch (IOException ex) {
            throw new IllegalStateException("Unable to load git info from location [" + GIT_RESOURCE_LOCATION + "]", ex);
        }
    }

    private static Map<String, Object> loadGitProperties(UrlResource urlResource) {
        StringBuilder sb = new StringBuilder();
        String line;
        String errorMsg;
        try (InputStream inputStream = urlResource.getInputStream();
             Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             BufferedReader br = new BufferedReader(reader)) {
            while ((line = br.readLine()) != null) {
                line = line.trim();
                // 如果当前行有字符并且不是注释则保存
                if (line.length() > 0 && !line.startsWith("#")) {
                    // 判断首字符是否是\uFEFF(UTF-16大端序、UTF-32)或\uFFFE(UTF-16小端序)，如果是则去掉
                    sb.append((line.charAt(0) == 65279 || line.charAt(0) == 65534) ? line.substring(1) : line);

                    // git.properties文件有两种格式，一种是json，一种是普通的properties格式，因此每一行增加分隔符
                    sb.append(SEPARATOR);
                }
            }
            // 去掉末尾的分隔符并转换
            if (sb.toString().length() > 0) {
                return parse(sb.toString().substring(0, sb.lastIndexOf(SEPARATOR)));
            }
            errorMsg = "The file has no valid data";
        } catch (Exception e) {
            log.error("Load {} from {} fail!", GIT_RESOURCE_LOCATION, urlResource.getURL(), e);
            errorMsg = "Version information could not be retrieved";
        }
        Map<String, Object> resultMap = new HashMap<>(2);
        resultMap.put("errorMsg", errorMsg);
        return resultMap;
    }

    private static Map<String, Object> parse(String str) {
        // 如果是json格式直接使用JSON转换成JSONObject对象
        if (str.startsWith("{") && str.endsWith("}")) {
            return JSON.parseObject(str.replaceAll(SEPARATOR, ""), Feature.OrderedField);
        }
        Map<String, Object> resultMap = new TreeMap<>();
        // 如果不是json格式则按普通properties格式转换
        for (String s : str.split(SEPARATOR)) {
            String[] properties = s.split("=");
            if (properties.length == 1) {
                resultMap.put(properties[0], "");
            } else if (properties.length == 2) {
                resultMap.put(properties[0], properties[1].replaceAll("\\\\:", ":"));
            }
        }
        return resultMap;
    }

}